package com.study.sample.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * es客户端封装工具
 */
@Slf4j
public class EsClientUtil {

    /**
     * 封装的客户端链接
     */
    private static RestHighLevelClient client() {
        return new RestHighLevelClient(RestClient.builder(new HttpHost("119.29.10.76", 9200, "http")));
    }

    /**
     * 执行es查询
     *
     * @param indexName           索引名称
     * @param beanClass           转换类
     * @param list                返回数据集合
     * @param searchSourceBuilder 条件构造器
     */
    public static <T> void getResultList(String indexName, Class<T> beanClass, List<T> list, SearchSourceBuilder searchSourceBuilder) throws IOException {
        // 创建查询请求对象，将查询对象配置到其中
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(searchSourceBuilder);
        // 执行查询，然后处理响应结果
        SearchResponse searchResponse = client().search(searchRequest, RequestOptions.DEFAULT);
        // 根据状态和数据条数验证是否返回了数据
        if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
            SearchHits hits = searchResponse.getHits();
            for (SearchHit hit : hits) {
                // 将 JSON 转换成对象
                // Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);
                // 将 JSON 转换成对象
                T bean = JSONUtil.toBean(hit.getSourceAsString(), beanClass);
                ReflectUtil.setFieldValue(bean, "id", hit.getId());
                list.add(bean);
            }
        }
    }


    /**
     * @param documents 结果集
     * @param beanClass bean
     * @param hits      es的结果集
     */
    public static <T> void convertResult(List<T> documents, Class<T> beanClass, SearchHit[] hits) {
        for (SearchHit hit : hits) {
            T document = JSONUtil.toBean(hit.getSourceAsString(), beanClass);
            ReflectUtil.setFieldValue(document, "id", hit.getId());
            // document.setId(Long.parseLong(hit.getId()));
            documents.add(document);
        }
    }

    public static <T> void convertResult(List<T> documents, Class<T> beanClass, SearchHit[] hits, List<String> fields) {
        if (CollUtil.isEmpty(fields)) throw new RuntimeException("传入高亮查询的属性字段为空");
        Text[] fragments = null;
        for (String field : fields) {
            for (SearchHit hit : hits) {
                Map<String, HighlightField> map = hit.getHighlightFields();
                if (CollUtil.isNotEmpty(map)) {
                    HighlightField highlightField = map.get(field);
                    fragments = highlightField.getFragments();
                    log.info("高亮名称: {}", highlightField.getFragments()[0].string());
                }
                // 替换掉原来的数据
                if (fragments != null && fragments.length > 0) {
                    StringBuilder highLightTxt = new StringBuilder();
                    for (Text fragment : fragments) {
                        highLightTxt.append(fragment);
                    }
                    T document = JSONUtil.toBean(hit.getSourceAsString(), beanClass);
                    ReflectUtil.setFieldValue(document, "id", hit.getId());
                    ReflectUtil.setFieldValue(document, field, highLightTxt);
                    // document.setId(Long.parseLong(hit.getId()));
                    documents.add(document);
                }
            }
        }
    }

    /**
     * terms:多个查询内容在一个字段中进行查询
     */
    public static <T> List<T> termsQuery(String indexName, String field, Object[] dataArgs, Class<T> beanClass) {
        // 查询的数据列表
        List<T> list = new ArrayList<>();
        try {
            // 构建查询条件（注意：termsQuery 支持多种格式查询，如 boolean、int、double、string 等，这里使用的是 string 的查询）
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.termsQuery(field, dataArgs));
            // 展示100条,默认只展示10条记录
            searchSourceBuilder.size(10);
            // 执行查询es数据
            getResultList(indexName, beanClass, list, searchSourceBuilder);

        } catch (IOException e) {
            log.info("查询异常: {}", e.getMessage());
            throw new RuntimeException(e);
        }
        return list;
    }

    /**
     * 内容在多字段中进行查询
     */
    public static <T> List<T> matchMultiQuery(String indexName, Class<T> beanClass, String[] fields, Object text) {

        // 查询的数据列表
        List<T> list = new ArrayList<>();
        try {
            // 构建查询条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

            // 设置查询条件
            searchSourceBuilder.query(QueryBuilders.multiMatchQuery(text, fields));

            // 执行查询es数据
            getResultList(indexName, beanClass, list, searchSourceBuilder);

        } catch (IOException e) {
            log.info("查询异常: {}", e.getMessage());
            throw new RuntimeException(e);
        }

        return list;
    }

    /**
     * 通配符查询(wildcard)：会对查询条件进行分词。还可以使用通配符 ?（任意单个字符） 和 * （0个或多个字符）
     * <p>
     * *：表示多个字符（0个或多个字符）
     * ?：表示单个字符
     */
    public static <T> List<T> wildcardQuery(String indexName, Class<T> beanClass, String field, String text) {
        // 查询的数据列表
        List<T> list = new ArrayList<>();
        try {
            // 构建查询条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.wildcardQuery(field, "*" + text + "*"));

            // 执行查询es数据
            getResultList(indexName, beanClass, list, searchSourceBuilder);

        } catch (IOException e) {
            log.info("查询异常: {}", e.getMessage());
            throw new RuntimeException(e);
        }

        return list;
    }

    /**
     * 模糊查询所有以 “三” 结尾的商品信息
     */
    public static <T> List<T> fuzzyQuery(String indexName, Class<T> beanClass, String field, String text) {
        // 查询的数据列表
        List<T> list = new ArrayList<>();
        try {
            // 构建查询条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.fuzzyQuery(field, text).fuzziness(Fuzziness.TWO));
            // 执行查询es数据
            getResultList(indexName, beanClass, list, searchSourceBuilder);

        } catch (IOException e) {
            log.info("查询异常: {}", e.getMessage());
            throw new RuntimeException(e);
        }
        return list;
    }
}
